home *** CD-ROM | disk | FTP | other *** search
- _GRAPHICS PROGRAMMING COLUMN_
- by Michael Abrash
-
-
- [LISTING ONE]
-
- /* Function to draw an antialiased line from (X0,Y0) to (X1,Y1), using an
- * antialiasing approach published by Xiaolin Wu in the July 1991 issue of
- * Computer Graphics. Requires that the palette be set up so that there
- * are NumLevels intensity levels of the desired drawing color, starting at
- * color BaseColor (100% intensity) and followed by (NumLevels-1) levels of
- * evenly decreasing intensity, with color (BaseColor+NumLevels-1) being 0%
- * intensity of the desired drawing color (black). This code is suitable for
- * use at screen resolutions, with lines typically no more than 1K long; for
- * longer lines, 32-bit error arithmetic must be used to avoid problems with
- * fixed-point inaccuracy. No clipping is performed in DrawWuLine; it must be
- * performed either at a higher level or in the DrawPixel function.
- * Tested with Borland C++ 3.0 in C compilation mode and the small model.
- */
- extern void DrawPixel(int, int, int);
-
- /* Wu antialiased line drawer.
- * (X0,Y0),(X1,Y1) = line to draw
- * BaseColor = color # of first color in block used for antialiasing, the
- * 100% intensity version of the drawing color
- * NumLevels = size of color block, with BaseColor+NumLevels-1 being the
- * 0% intensity version of the drawing color
- * IntensityBits = log base 2 of NumLevels; the # of bits used to describe
- * the intensity of the drawing color. 2**IntensityBits==NumLevels
- */
- void DrawWuLine(int X0, int Y0, int X1, int Y1, int BaseColor, int NumLevels,
- unsigned int IntensityBits)
- {
- unsigned int IntensityShift, ErrorAdj, ErrorAcc;
- unsigned int ErrorAccTemp, Weighting, WeightingComplementMask;
- int DeltaX, DeltaY, Temp, XDir;
-
- /* Make sure the line runs top to bottom */
- if (Y0 > Y1) {
- Temp = Y0; Y0 = Y1; Y1 = Temp;
- Temp = X0; X0 = X1; X1 = Temp;
- }
- /* Draw the initial pixel, which is always exactly intersected by
- the line and so needs no weighting */
- DrawPixel(X0, Y0, BaseColor);
-
- if ((DeltaX = X1 - X0) >= 0) {
- XDir = 1;
- } else {
- XDir = -1;
- DeltaX = -DeltaX; /* make DeltaX positive */
- }
- /* Special-case horizontal, vertical, and diagonal lines, which
- require no weighting because they go right through the center of
- every pixel */
- if ((DeltaY = Y1 - Y0) == 0) {
- /* Horizontal line */
- while (DeltaX-- != 0) {
- X0 += XDir;
- DrawPixel(X0, Y0, BaseColor);
- }
- return;
- }
- if (DeltaX == 0) {
- /* Vertical line */
- do {
- Y0++;
- DrawPixel(X0, Y0, BaseColor);
- } while (--DeltaY != 0);
- return;
- }
- if (DeltaX == DeltaY) {
- /* Diagonal line */
- do {
- X0 += XDir;
- Y0++;
- DrawPixel(X0, Y0, BaseColor);
- } while (--DeltaY != 0);
- return;
- }
- /* Line is not horizontal, diagonal, or vertical */
- ErrorAcc = 0; /* initialize the line error accumulator to 0 */
- /* # of bits by which to shift ErrorAcc to get intensity level */
- IntensityShift = 16 - IntensityBits;
- /* Mask used to flip all bits in an intensity weighting, producing the
- result (1 - intensity weighting) */
- WeightingComplementMask = NumLevels - 1;
- /* Is this an X-major or Y-major line? */
- if (DeltaY > DeltaX) {
- /* Y-major line; calculate 16-bit fixed-point fractional part of a
- pixel that X advances each time Y advances 1 pixel, truncating the
- result so that we won't overrun the endpoint along the X axis */
- ErrorAdj = ((unsigned long) DeltaX << 16) / (unsigned long) DeltaY;
- /* Draw all pixels other than the first and last */
- while (--DeltaY) {
- ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */
- ErrorAcc += ErrorAdj; /* calculate error for next pixel */
- if (ErrorAcc <= ErrorAccTemp) {
- /* The error accumulator turned over, so advance the X coord */
- X0 += XDir;
- }
- Y0++; /* Y-major, so always advance Y */
- /* The IntensityBits most significant bits of ErrorAcc give us the
- intensity weighting for this pixel, and the complement of the
- weighting for the paired pixel */
- Weighting = ErrorAcc >> IntensityShift;
- DrawPixel(X0, Y0, BaseColor + Weighting);
- DrawPixel(X0 + XDir, Y0,
- BaseColor + (Weighting ^ WeightingComplementMask));
- }
- /* Draw the final pixel, which is always exactly intersected by the line
- and so needs no weighting */
- DrawPixel(X1, Y1, BaseColor);
- return;
- }
- /* It's an X-major line; calculate 16-bit fixed-point fractional part of a
- pixel that Y advances each time X advances 1 pixel, truncating the
- result to avoid overrunning the endpoint along the X axis */
- ErrorAdj = ((unsigned long) DeltaY << 16) / (unsigned long) DeltaX;
- /* Draw all pixels other than the first and last */
- while (--DeltaX) {
- ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */
- ErrorAcc += ErrorAdj; /* calculate error for next pixel */
- if (ErrorAcc <= ErrorAccTemp) {
- /* The error accumulator turned over, so advance the Y coord */
- Y0++;
- }
- X0 += XDir; /* X-major, so always advance X */
- /* The IntensityBits most significant bits of ErrorAcc give us the
- intensity weighting for this pixel, and the complement of the
- weighting for the paired pixel */
- Weighting = ErrorAcc >> IntensityShift;
- DrawPixel(X0, Y0, BaseColor + Weighting);
- DrawPixel(X0, Y0 + 1,
- BaseColor + (Weighting ^ WeightingComplementMask));
- }
- /* Draw the final pixel, which is always exactly intersected by the line
- and so needs no weighting */
- DrawPixel(X1, Y1, BaseColor);
- }
-
-
- [LISTING TWO]
- /* Sample line-drawing program to demonstrate Wu antialiasing. Also draws
- * non-antialiased lines for comparison.
- * Tested with Borland C++ 3.0 in C compilation mode and the small model.
- */
- #include <dos.h>
- #include <conio.h>
-
- void SetPalette(struct WuColor *);
- extern void DrawWuLine(int, int, int, int, int, int, unsigned int);
- extern void DrawLine(int, int, int, int, int);
- extern void SetMode(void);
- extern int ScreenWidthInPixels; /* screen dimension globals */
- extern int ScreenHeightInPixels;
-
- #define NUM_WU_COLORS 2 /* # of colors we'll do antialiased drawing with */
- struct WuColor { /* describes one color used for antialiasing */
- int BaseColor; /* # of start of palette intensity block in DAC */
- int NumLevels; /* # of intensity levels */
- int IntensityBits; /* IntensityBits == log2 NumLevels */
- int MaxRed; /* red component of color at full intensity */
- int MaxGreen; /* green component of color at full intensity */
- int MaxBlue; /* blue component of color at full intensity */
- };
- enum {WU_BLUE=0, WU_WHITE=1}; /* drawing colors */
- struct WuColor WuColors[NUM_WU_COLORS] = /* blue and white */
- {{192, 32, 5, 0, 0, 0x3F}, {224, 32, 5, 0x3F, 0x3F, 0x3F}};
-
- void main()
- {
- int CurrentColor, i;
- union REGS regset;
-
- /* Draw Wu-antialiased lines in all directions */
- SetMode();
- SetPalette(WuColors);
- for (i=5; i<ScreenWidthInPixels; i += 10) {
- DrawWuLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
- ScreenHeightInPixels/5, i, ScreenHeightInPixels-1,
- WuColors[WU_BLUE].BaseColor, WuColors[WU_BLUE].NumLevels,
- WuColors[WU_BLUE].IntensityBits);
- }
- for (i=0; i<ScreenHeightInPixels; i += 10) {
- DrawWuLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10, i/5, 0, i,
- WuColors[WU_BLUE].BaseColor, WuColors[WU_BLUE].NumLevels,
- WuColors[WU_BLUE].IntensityBits);
- }
- for (i=0; i<ScreenHeightInPixels; i += 10) {
- DrawWuLine(ScreenWidthInPixels/2+ScreenWidthInPixels/10, i/5,
- ScreenWidthInPixels-1, i, WuColors[WU_BLUE].BaseColor,
- WuColors[WU_BLUE].NumLevels, WuColors[WU_BLUE].IntensityBits);
- }
- for (i=0; i<ScreenWidthInPixels; i += 10) {
- DrawWuLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
- ScreenHeightInPixels, i, 0, WuColors[WU_WHITE].BaseColor,
- WuColors[WU_WHITE].NumLevels,
- WuColors[WU_WHITE].IntensityBits);
- }
- getch(); /* wait for a key press */
-
- /* Now clear the screen and draw non-antialiased lines */
- SetMode();
- SetPalette(WuColors);
- for (i=0; i<ScreenWidthInPixels; i += 10) {
- DrawLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
- ScreenHeightInPixels/5, i, ScreenHeightInPixels-1,
- WuColors[WU_BLUE].BaseColor);
- }
- for (i=0; i<ScreenHeightInPixels; i += 10) {
- DrawLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10, i/5, 0, i,
- WuColors[WU_BLUE].BaseColor);
- }
- for (i=0; i<ScreenHeightInPixels; i += 10) {
- DrawLine(ScreenWidthInPixels/2+ScreenWidthInPixels/10, i/5,
- ScreenWidthInPixels-1, i, WuColors[WU_BLUE].BaseColor);
- }
- for (i=0; i<ScreenWidthInPixels; i += 10) {
- DrawLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
- ScreenHeightInPixels, i, 0, WuColors[WU_WHITE].BaseColor);
- }
- getch(); /* wait for a key press */
-
- regset.x.ax = 0x0003; /* AL = 3 selects 80x25 text mode */
- int86(0x10, ®set, ®set); /* return to text mode */
- }
-
- /* Sets up the palette for antialiasing with the specified colors.
- * Intensity steps for each color are scaled from the full desired intensity
- * of the red, green, and blue components for that color down to 0%
- * intensity; each step is rounded to the nearest integer. Colors are
- * corrected for a gamma of 2.3. The values that the palette is programmed
- * with are hardwired for the VGA's 6 bit per color DAC.
- */
- void SetPalette(struct WuColor * WColors)
- {
- int i, j;
- union REGS regset;
- struct SREGS sregset;
- static unsigned char PaletteBlock[256][3]; /* 256 RGB entries */
- /* Gamma-corrected DAC color components for 64 linear levels from 0% to
- 100% intensity */
- static unsigned char GammaTable[] = {
- 0, 10, 14, 17, 19, 21, 23, 24, 26, 27, 28, 29, 31, 32, 33, 34,
- 35, 36, 37, 37, 38, 39, 40, 41, 41, 42, 43, 44, 44, 45, 46, 46,
- 47, 48, 48, 49, 49, 50, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55,
- 56, 56, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63};
-
- for (i=0; i<NUM_WU_COLORS; i++) {
- for (j=0; j<WColors[i].NumLevels; j++) {
- PaletteBlock[j][0] = GammaTable[((double)WColors[i].MaxRed * (1.0 -
- (double)j / (double)(WColors[i].NumLevels - 1))) + 0.5];
- PaletteBlock[j][1] = GammaTable[((double)WColors[i].MaxGreen * (1.0 -
- (double)j / (double)(WColors[i].NumLevels - 1))) + 0.5];
- PaletteBlock[j][2] = GammaTable[((double)WColors[i].MaxBlue * (1.0 -
- (double)j / (double)(WColors[i].NumLevels - 1))) + 0.5];
- }
- /* Now set up the palette to do Wu antialiasing for this color */
- regset.x.ax = 0x1012; /* set block of DAC registers function */
- regset.x.bx = WColors[i].BaseColor; /* first DAC location to load */
- regset.x.cx = WColors[i].NumLevels; /* # of DAC locations to load */
- regset.x.dx = (unsigned int)PaletteBlock; /* offset of array from which
- to load RGB settings */
- sregset.es = _DS; /* segment of array from which to load settings */
- int86x(0x10, ®set, ®set, &sregset); /* load the palette block */
- }
- }
-
-
- [LISTING THREE]
-
-
- /* VGA mode 13h pixel-drawing and mode set functions.
- * Tested with Borland C++ 3.0 in C compilation mode and the small model.
- */
- #include <dos.h>
-
- /* Screen dimension globals, used in main program to scale. */
- int ScreenWidthInPixels = 320;
- int ScreenHeightInPixels = 200;
-
- /* Mode 13h draw pixel function. */
- void DrawPixel(int X, int Y, int Color)
- {
- #define SCREEN_SEGMENT 0xA000
- unsigned char far *ScreenPtr;
-
- FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
- FP_OFF(ScreenPtr) = (unsigned int) Y * ScreenWidthInPixels + X;
- *ScreenPtr = Color;
- }
-
- /* Mode 13h mode-set function. */
- void SetMode()
- {
- union REGS regset;
-
- /* Set to 320x200 256-color graphics mode */
- regset.x.ax = 0x0013;
- int86(0x10, ®set, ®set);
- }
-
-
- [LISTING FOUR]
-
- /* Function to draw a non-antialiased line from (X0,Y0) to (X1,Y1), using a
- * simple fixed-point error accumulation approach.
- * Tested with Borland C++ 3.0 in C compilation mode and the small model.
- */
- extern void DrawPixel(int, int, int);
-
- /* Non-antialiased line drawer.
- * (X0,Y0),(X1,Y1) = line to draw, Color = color in which to draw
- */
- void DrawLine(int X0, int Y0, int X1, int Y1, int Color)
- {
- unsigned long ErrorAcc, ErrorAdj;
- int DeltaX, DeltaY, XDir, Temp;
-
- /* Make sure the line runs top to bottom */
- if (Y0 > Y1) {
- Temp = Y0; Y0 = Y1; Y1 = Temp;
- Temp = X0; X0 = X1; X1 = Temp;
- }
- DrawPixel(X0, Y0, Color); /* draw the initial pixel */
- if ((DeltaX = X1 - X0) >= 0) {
- XDir = 1;
- } else {
- XDir = -1;
- DeltaX = -DeltaX; /* make DeltaX positive */
- }
- if ((DeltaY = Y1 - Y0) == 0) /* done if only one point in the line */
- if (DeltaX == 0) return;
-
- ErrorAcc = 0x8000; /* initialize line error accumulator to .5, so we can
- advance when we get halfway to the next pixel */
- /* Is this an X-major or Y-major line? */
- if (DeltaY > DeltaX) {
- /* Y-major line; calculate 16-bit fixed-point fractional part of a
- pixel that X advances each time Y advances 1 pixel */
- ErrorAdj = ((((unsigned long)DeltaX << 17) / (unsigned long)DeltaY) +
- 1) >> 1;
- /* Draw all pixels between the first and last */
- do {
- ErrorAcc += ErrorAdj; /* calculate error for this pixel */
- if (ErrorAcc & ~0xFFFFL) {
- /* The error accumulator turned over, so advance the X coord */
- X0 += XDir;
- ErrorAcc &= 0xFFFFL; /* clear integer part of result */
- }
- Y0++; /* Y-major, so always advance Y */
- DrawPixel(X0, Y0, Color);
- } while (--DeltaY);
- return;
- }
- /* It's an X-major line; calculate 16-bit fixed-point fractional part of a
- pixel that Y advances each time X advances 1 pixel */
- ErrorAdj = ((((unsigned long)DeltaY << 17) / (unsigned long)DeltaX) +
- 1) >> 1;
- /* Draw all remaining pixels */
- do {
- ErrorAcc += ErrorAdj; /* calculate error for this pixel */
- if (ErrorAcc & ~0xFFFFL) {
- /* The error accumulator turned over, so advance the Y coord */
- Y0++;
- ErrorAcc &= 0xFFFFL; /* clear integer part of result */
- }
- X0 += XDir; /* X-major, so always advance X */
- DrawPixel(X0, Y0, Color);
- } while (--DeltaX);
- }
-
-
- [LISTING FIVE]
-
- /* Mode set and pixel-drawing functions for the 640x480 256-color mode of
- * Tseng Labs ET4000-based SuperVGAs.
- * Tested with Borland C++ 3.0 in C compilation mode and the small model.
- */
- #include <dos.h>
-
- /* Screen dimension globals, used in main program to scale */
- int ScreenWidthInPixels = 640;
- int ScreenHeightInPixels = 480;
-
- /* ET4000 640x480 256-color draw pixel function. */
- void DrawPixel(int X, int Y, int Color)
- {
- #define SCREEN_SEGMENT 0xA000
- #define GC_SEGMENT_SELECT 0x3CD /* ET4000 segment (bank) select reg */
- unsigned char far *ScreenPtr;
- unsigned int Bank;
- unsigned long BitmapAddress;
-
- /* Full bitmap address of pixel, as measured from address 0 to 0xFFFFF */
- BitmapAddress = (unsigned long) Y * ScreenWidthInPixels + X;
- /* Bank # is upper word of bitmap addr */
- Bank = BitmapAddress >> 16;
- /* Upper nibble is read bank #, lower nibble is write bank # */
- outp(GC_SEGMENT_SELECT, (Bank << 4) | Bank);
- /* Draw into the bank */
- FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
- FP_OFF(ScreenPtr) = (unsigned int) BitmapAddress;
- *ScreenPtr = Color;
- }
-
- /* ET4000 640x480 256-color mode-set function. */
- void SetMode()
- {
- union REGS regset;
-
- /* Set to 640x480 256-color graphics mode */
- regset.x.ax = 0x002E;
- int86(0x10, ®set, ®set);
- }
-
-
-
- [LISTING SIX]
-
- ; Inner loop for drawing Y-major lines from Wu-antialiased line drawer.
- YMajorLoop:
- add dx,bp ;calculate error for next pixel
- jnc NoXAdvance ;not time to step in X yet
- ;the error accumulator turned over,
- ; so advance the X coord
- add si,bx ;add XDir to the pixel pointer
- NoXAdvance:
- add si,SCREEN_WIDTH_IN_BYTES ;Y-major, so always advance Y
- ; The IntensityBits most significant bits of ErrorAcc give us the intensity
- ; weighting for this pixel, and the complement of the weighting for the
- ; paired pixel.
- mov ah,dh ;msb of ErrorAcc
- shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
- add ah,al ;BaseColor + Weighting
- mov [si],ah ;DrawPixel(X, Y, BaseColor + Weighting);
- mov ah,dh ;msb of ErrorAcc
- shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
- xor ah,ch ;Weighting ^ WeightingComplementMask
- add ah,al ;BaseColor + (Weighting ^ WeightingComplementMask)
- mov [si+bx],ah ;DrawPixel(X+XDir, Y,
- ; BaseColor + (Weighting ^ WeightingComplementMask));
- dec di ;--DeltaY
- jnz YMajorLoop
-
-
- [WU ANTIALIASING]
-
- ; C near-callable function to draw an antialiased line from
- ; (X0,Y0) to (X1,Y1), in mode 13h, the VGA's standard 320x200 256-color
- ; mode. Uses an antialiasing approach published by Xiaolin Wu in the July
- ; 1991 issue of Computer Graphics. Requires that the palette be set up so
- ; that there are NumLevels intensity levels of the desired drawing color,
- ; starting at color BaseColor (100% intensity) and followed by (NumLevels-1)
- ; levels of evenly decreasing intensity, with color (BaseColor+NumLevels-1)
- ; being 0% intensity of the desired drawing color (black). No clipping is
- ; performed in DrawWuLine. Handles a maximum of 256 intensity levels per
- ; antialiased color. This code is suitable for use at screen resolutions,
- ; with lines typically no more than 1K long; for longer lines, 32-bit error
- ; arithmetic must be used to avoid problems with fixed-point inaccuracy.
- ; Tested with TASM 3.0.
- ;
- ; C near-callable as:
- ; void DrawWuLine(int X0, int Y0, int X1, int Y1, int BaseColor,
- ; int NumLevels, unsigned int IntensityBits);
-
- SCREEN_WIDTH_IN_BYTES equ 320 ;# of bytes from the start of one scan line
- ; to the start of the next
- SCREEN_SEGMENT equ 0a000h ;segment in which screen memory resides
-
- ; Parameters passed in stack frame.
- parms struc
- dw 2 dup (?) ;pushed BP and return address
- X0 dw ? ;X coordinate of line start point
- Y0 dw ? ;Y coordinate of line start point
- X1 dw ? ;X coordinate of line end point
- Y1 dw ? ;Y coordinate of line end point
- BaseColor dw ? ;color # of first color in block used for
- ; antialiasing, the 100% intensity version of the
- ; drawing color
- NumLevels dw ? ;size of color block, with BaseColor+NumLevels-1
- ; being the 0% intensity version of the drawing color
- ; (maximum NumLevels = 256)
- IntensityBits dw ? ;log base 2 of NumLevels; the # of bits used to
- ; describe the intensity of the drawing color.
- ; 2**IntensityBits==NumLevels
- ; (maximum IntensityBits = 8)
- parms ends
-
- .model small
- .code
- ; Screen dimension globals, used in main program to scale.
- _ScreenWidthInPixels dw 320
- _ScreenHeightInPixels dw 200
-
- .code
- public _DrawWuLine
- _DrawWuLine proc near
- push bp ;preserve caller's stack frame
- mov bp,sp ;point to local stack frame
- push si ;preserve C's register variables
- push di
- push ds ;preserve C's default data segment
- cld ;make string instructions increment their pointers
-
- ; Make sure the line runs top to bottom.
- mov si,[bp].X0
- mov ax,[bp].Y0
- cmp ax,[bp].Y1 ;swap endpoints if necessary to ensure that
- jna NoSwap ; Y0 <= Y1
- xchg [bp].Y1,ax
- mov [bp].Y0,ax
- xchg [bp].X1,si
- mov [bp].X0,si
- NoSwap:
-
- ; Draw the initial pixel, which is always exactly intersected by the line
- ; and so needs no weighting.
- mov dx,SCREEN_SEGMENT
- mov ds,dx ;point DS to the screen segment
- mov dx,SCREEN_WIDTH_IN_BYTES
- mul dx ;Y0 * SCREEN_WIDTH_IN_BYTES yields the offset
- ; of the start of the row start the initial
- ; pixel is on
- add si,ax ;point DS:SI to the initial pixel
- mov al,byte ptr [bp].BaseColor ;color with which to draw
- mov [si],al ;draw the initial pixel
-
- mov bx,1 ;XDir = 1; assume DeltaX >= 0
- mov cx,[bp].X1
- sub cx,[bp].X0 ;DeltaX; is it >= 1?
- jns DeltaXSet ;yes, move left->right, all set
- ;no, move right->left
- neg cx ;make DeltaX positive
- neg bx ;XDir = -1
- DeltaXSet:
-
- ; Special-case horizontal, vertical, and diagonal lines, which require no
- ; weighting because they go right through the center of every pixel.
- mov dx,[bp].Y1
- sub dx,[bp].Y0 ;DeltaY; is it 0?
- jnz NotHorz ;no, not horizontal
- ;yes, is horizontal, special case
- and bx,bx ;draw from left->right?
- jns DoHorz ;yes, all set
- std ;no, draw right->left
- DoHorz:
- lea di,[bx+si] ;point DI to next pixel to draw
- mov ax,ds
- mov es,ax ;point ES:DI to next pixel to draw
- mov al,byte ptr [bp].BaseColor ;color with which to draw
- ;CX = DeltaX at this point
- rep stosb ;draw the rest of the horizontal line
- cld ;restore default direction flag
- jmp Done ;and we're done
-
- align 2
- NotHorz:
- and cx,cx ;is DeltaX 0?
- jnz NotVert ;no, not a vertical line
- ;yes, is vertical, special case
- mov al,byte ptr [bp].BaseColor ;color with which to draw
- VertLoop:
- add si,SCREEN_WIDTH_IN_BYTES ;point to next pixel to draw
- mov [si],al ;draw the next pixel
- dec dx ;--DeltaY
- jnz VertLoop
- jmp Done ;and we're done
-
- align 2
- NotVert:
- cmp cx,dx ;DeltaX == DeltaY?
- jnz NotDiag ;no, not diagonal
- ;yes, is diagonal, special case
- mov al,byte ptr [bp].BaseColor ;color with which to draw
- DiagLoop:
- lea si,[si+SCREEN_WIDTH_IN_BYTES+bx]
- ;advance to next pixel to draw by
- ; incrementing Y and adding XDir to X
- mov [si],al ;draw the next pixel
- dec dx ;--DeltaY
- jnz DiagLoop
- jmp Done ;and we're done
-
- ; Line is not horizontal, diagonal, or vertical.
- align 2
- NotDiag:
- ; Is this an X-major or Y-major line?
- cmp dx,cx
- jb XMajor ;it's X-major
-
- ; It's a Y-major line. Calculate the 16-bit fixed-point fractional part of a
- ; pixel that X advances each time Y advances 1 pixel, truncating the result
- ; to avoid overrunning the endpoint along the X axis.
- xchg dx,cx ;DX = DeltaX, CX = DeltaY
- sub ax,ax ;make DeltaX 16.16 fixed-point value in DX:AX
- div cx ;AX = (DeltaX << 16) / DeltaY. Won't overflow
- ; because DeltaX < DeltaY
- mov di,cx ;DI = DeltaY (loop count)
- sub si,bx ;back up the start X by 1, as explained below
- mov dx,-1 ;initialize the line error accumulator to -1,
- ; so that it will turn over immediately and
- ; advance X to the start X. This is necessary
- ; properly to bias error sums of 0 to mean
- ; "advance next time" rather than "advance
- ; this time," so that the final error sum can
- ; never cause drawing to overrun the final X
- ; coordinate (works in conjunction with
- ; truncating ErrorAdj, to make sure X can't
- ; overrun)
- mov cx,8 ;CL = # of bits by which to shift
- sub cx,[bp].IntensityBits ; ErrorAcc to get intensity level (8
- ; instead of 16 because we work only
- ; with the high byte of ErrorAcc)
- mov ch,byte ptr [bp].NumLevels ;mask used to flip all bits in an
- dec ch ; intensity weighting, producing
- ; result (1 - intensity weighting)
- mov bp,BaseColor[bp] ;***stack frame not available***
- ;***from now on ***
- xchg bp,ax ;BP = ErrorAdj, AL = BaseColor,
- ; AH = scratch register
-
- ; Draw all remaining pixels.
- YMajorLoop:
- add dx,bp ;calculate error for next pixel
- jnc NoXAdvance ;not time to step in X yet
- ;the error accumulator turned over,
- ; so advance the X coord
- add si,bx ;add XDir to the pixel pointer
- NoXAdvance:
- add si,SCREEN_WIDTH_IN_BYTES ;Y-major, so always advance Y
-
- ; The IntensityBits most significant bits of ErrorAcc give us the intensity
- ; weighting for this pixel, and the complement of the weighting for the
- ; paired pixel.
- mov ah,dh ;msb of ErrorAcc
- shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
- add ah,al ;BaseColor + Weighting
- mov [si],ah ;DrawPixel(X, Y, BaseColor + Weighting);
- mov ah,dh ;msb of ErrorAcc
- shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
- xor ah,ch ;Weighting ^ WeightingComplementMask
- add ah,al ;BaseColor + (Weighting ^ WeightingComplementMask)
- mov [si+bx],ah ;DrawPixel(X+XDir, Y,
- ; BaseColor + (Weighting ^ WeightingComplementMask));
- dec di ;--DeltaY
- jnz YMajorLoop
- jmp Done ;we're done with this line
-
- ; It's an X-major line.
- align 2
- XMajor:
- ; Calculate the 16-bit fixed-point fractional part of a pixel that Y advances
- ; each time X advances 1 pixel, truncating the result to avoid overrunning
- ; the endpoint along the X axis.
- sub ax,ax ;make DeltaY 16.16 fixed-point value in DX:AX
- div cx ;AX = (DeltaY << 16) / Deltax. Won't overflow
- ; because DeltaY < DeltaX
- mov di,cx ;DI = DeltaX (loop count)
- sub si,SCREEN_WIDTH_IN_BYTES ;back up the start X by 1, as
- ; explained below
- mov dx,-1 ;initialize the line error accumulator to -1,
- ; so that it will turn over immediately and
- ; advance Y to the start Y. This is necessary
- ; properly to bias error sums of 0 to mean
- ; "advance next time" rather than "advance
- ; this time," so that the final error sum can
- ; never cause drawing to overrun the final Y
- ; coordinate (works in conjunction with
- ; truncating ErrorAdj, to make sure Y can't
- ; overrun)
- mov cx,8 ;CL = # of bits by which to shift
- sub cx,[bp].IntensityBits ; ErrorAcc to get intensity level (8
- ; instead of 16 because we work only
- ; with the high byte of ErrorAcc)
- mov ch,byte ptr [bp].NumLevels ;mask used to flip all bits in an
- dec ch ; intensity weighting, producing
- ; result (1 - intensity weighting)
- mov bp,BaseColor[bp] ;***stack frame not available***
- ;***from now on ***
- xchg bp,ax ;BP = ErrorAdj, AL = BaseColor,
- ; AH = scratch register
- ; Draw all remaining pixels.
- XMajorLoop:
- add dx,bp ;calculate error for next pixel
- jnc NoYAdvance ;not time to step in Y yet
- ;the error accumulator turned over,
- ; so advance the Y coord
- add si,SCREEN_WIDTH_IN_BYTES ;advance Y
- NoYAdvance:
- add si,bx ;X-major, so add XDir to the pixel pointer
-
- ; The IntensityBits most significant bits of ErrorAcc give us the intensity
- ; weighting for this pixel, and the complement of the weighting for the
- ; paired pixel.
- mov ah,dh ;msb of ErrorAcc
- shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
- add ah,al ;BaseColor + Weighting
- mov [si],ah ;DrawPixel(X, Y, BaseColor + Weighting);
- mov ah,dh ;msb of ErrorAcc
- shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
- xor ah,ch ;Weighting ^ WeightingComplementMask
- add ah,al ;BaseColor + (Weighting ^ WeightingComplementMask)
- mov [si+SCREEN_WIDTH_IN_BYTES],ah
- ;DrawPixel(X, Y+SCREEN_WIDTH_IN_BYTES,
- ; BaseColor + (Weighting ^ WeightingComplementMask));
- dec di ;--DeltaX
- jnz XMajorLoop
-
- Done: ;we're done with this line
- pop ds ;restore C's default data segment
- pop di ;restore C's register variables
- pop si
- pop bp ;restore caller's stack frame
- ret ;done
- _DrawWuLine endp
- end
-
-
-